cc-pocket · phase 2 · proofs

A Master Proofs v1

Four frames that stress the core master: the light palette, dynamic type at 200%, an approval under queue and time pressure, and a peer that supplies almost nothing. Tokens, geometry and component anatomy are inherited unchanged from A Master Core v1.

4 proofs
390 · 360 wide
1:1 scale
no new tokens
04 · Sessions · light · 390×844
9:41

Sessions

Panda · MacBook Pro · online
cc-pocket · feat/auth-refactor
~/proj/app/cc-pocket
Active
Refactor auth module
Review the concurrency around the refresh mutex before I open the PR.
Claude · feat/auth-refactor
Approval required Review
Recent
Fix flaky socket test
The reconnect test still fails intermittently on CI.
Codex · fix/socket-test · 2h ago
Release notes 1.6
Summarize the user-visible changes from the last 12 commits.
Claude · main · yesterday
+New session
Proves
Geometry is identical to the dark master — same 24pt gutter, same row rhythm, same marks. Only token values change.

Separation still comes from hairlines at 16% ink and weight contrast. No card, no shadow, no paper tint anywhere in the list.

Terracotta darkens to #A9482A in light so the Review label clears 4.5:1; the attention diamond and its border clear 3:1 against the base.

Still no run duration, and the pending approval is still the only filled control on the screen.
05 · Chat · 360×780 · 200% type
9:41
Refactor auth module
Claude · default permission · Panda
cc-pocket · feat/auth-refactor
Approval required
Upload coverage to Codecov Review
Running · 1 approval blocking another action
You
add a unit test for the stream parser
Claude
The parser now emits exactly one event when a frame is split across chunks.
Tool
Bash ./gradlew :protocol:test 42 passed
+ Message Claude
Composer enabled · messages queue behind the approval
Anatomy · context expanded · same frame
Context
Panda · MacBook Pro
cc-pocket
feat/auth-refactor
~/proj/app/cc-pocket
Expanded region, drawn at frame width. Path wraps to complete lines beside a reserved 48dp copy column; the region scrolls inside itself and collapses back to the summary above.
Proves
Type is scaled up, never down. Captured state: context collapsed. Title, agent and the approval state block stay sticky; the verbose context lines live in an expandable region below them, so the header is never permanently pinned in full. The collapsed summary still carries agent, machine, project and branch.

Collapsing returns roughly 210pt to the stream, which is why the complete User turn and the start of Claude's reply are both visible here above the fade, with the composer reachable and its state written out.

Expanded context is drawn in the callout above, because at 200% type the expanded region and a usable stream cannot share one 780pt viewport. There the path holds a dedicated 48dp copy column and wraps to complete lines at 24pt mono — no ellipsis, no hidden characters — and the region scrolls internally rather than pushing the stream away.

No row has a fixed height, no timestamps are invented, and Running stays subordinate to Approval required.
06 · Approval · stress · 360×780 · body scrolled · evidence expanded
9:41
Refactor auth module
cc-pocket · feat/auth-refactor
Approval required 2 of 3
8s automatically denied when time runs out
Runs the full project test suite including the integration tier, then uploads the merged coverage report to Codecov and posts a status back to the pull request.
Run command Bash
Medium risk external upload
./gradlew test integrationTest --stacktrace && bash scripts/merge-coverage.sh --out build/reports/coverage.xml && bash scripts/upload-coverage.sh --token "$CODECOV_TOKEN"
Permission This task only
Effects Runs tests in cc-pocket
Uploads coverage externally
Project ~/proj/app/cc-pocket-android-client/app
Evidencescript contents, destination and affected files
Destination
https://codecov.io/upload/v4 · api token from env
Script · scripts/upload-coverage.sh
set -euo pipefail
curl -s https://codecov.io/bash > /tmp/cc.sh
bash /tmp/cc.sh -f build/reports/coverage.xml
Affected files · 3
build/reports/coverage.xml
build/test-results/test/*.xml
/tmp/cc.sh
Recommended: Allow for this task
The command stays in this project, but uploads coverage to Codecov.
Deny
Allow for this task
Proves
Captured mid-scroll with Evidence expanded: the body sits at the Evidence section, showing Destination, the script contents and the start of the three affected files. Fades at both edges and the scroll track make the internal scrolling unambiguous.

Worst case holds the three zones. The header keeps Approval required, queue 2 of 3 and 8s pinned; the consequence, command, Permission and Effects rows stay above in the same scroll body; the decision bar never moves.

Long strings break rather than truncate — command, project path and destination all wrap on characters, because a half-shown command is worse than a tall one.

The recommendation is present because the daemon supplied one, and its reason is the auditable sentence beneath it. On a danger-class request that block flips to Deny and the fill moves with it.

No grabber, no swipe, no scrim dismissal. At 0s the request is denied, and the next queued request takes its place.
07 · Approval · legacy peer · light · 390×844
9:41
Refactor auth module
cc-pocket · feat/auth-refactor
Approval required
Run command Bash
./gradlew test && bash scripts/upload-coverage.sh
Permission This task only
Project ~/proj/app/cc-pocket
Additional details unavailable from this client version.
Deny
Allow for this task
Proves
The peer supplied kind, tool, command, project and permission duration. Everything else is gone from the screen rather than blanked, greyed or guessed: no consequence sentence, no risk badge, no effects, no queue, no countdown, no recommendation.

The sheet shrinks to its content. Nothing reserves space for data that never arrived, so there are no empty rows.

The compatibility note is muted body text in the neutral secondary colour, deliberately not a badge and not adjacent to the actions, so it can never read as evidence or as advice.

Without a countdown there is no timeout: the request stays open until the user decides or the daemon withdraws it. Absence of risk data is never presented as safety.
R

Responsive rules · 360–430dp, up to 200% type

Gutters and rhythm

24pt gutter at 390dp and above, 20pt below 375dp. Vertical rhythm never compresses — the 8pt block spacing is fixed, and long content scrolls instead.

Height and wrapping

No fixed row heights anywhere. Titles wrap to three lines before ellipsis. Metadata splits from one line into one fact per line past ~150% type. Paths and commands break on characters, never truncate.

Controls

48dp minimum always; controls grow with type but never shrink. Inline actions promote to full-width when their label would clip. Decision bar goes to stacked full-width buttons past 200%, Deny first in the reading order.

Pinned regions

Screen header, state block, composer and decision bar stay reachable at every size. Composer respects IME and system insets. If a pinned region would exceed 45% of the viewport it scrolls internally rather than stealing the body.

D

Data-truth contract

The client renders what it is given and nothing else.
Field
Renders when
Missing behaviour
state
Always. Exactly one state per session, chosen by priority order.
Not possible. An unrecognised state from a newer peer degrades to the row with no mark and no action.
runStartedAt
Supplied and the session is Running — then duration may be shown beside the state.
Show Running with no duration. Never derive from receipt time, first message or session creation.
chat timestamp
Per event, only when the event carries one.
Omit the timestamp slot entirely. Order is preserved by sequence, not by invented clock values.
consequenceSummary
Supplied by the daemon from an auditable source. Becomes the primary heading of the sheet.
Sheet leads with request kind and tool instead. The client never summarises a command itself.
riskLevel · riskReason
Level renders as a badge when supplied; the reason renders beside it when supplied.
Badge omitted. Absence is shown as absence — never as low risk, and never as a green or neutral safety mark.
queue n/m
Capability-gated. Both position and total must be supplied together.
Header drops the counter and keeps its state label. No “1 of 1” fallback.
countdown · expiresAt
Capability-gated. Timer, bar and the fail-closed sentence render together or not at all.
No timer, no bar, no timeout claim. The request stays open until decided or withdrawn.
recommendation
Capability-gated and only with a reason string. Shown directly above the decision bar.
Block omitted and both actions render neutral. A recommendation without a reason is discarded, not displayed.
S

Safety invariants

State priority
Approval required › Answer required › Failure › Running › New result › Complete

One state owns the row and the action. Lower states may appear only as a qualifying line, as Running does under an open approval.

No client invention

Consequence, risk and recommendation originate in the daemon and are auditable. The client may format them; it may not compose, infer or soften them.

Fail closed

A countdown that reaches zero denies. Loss of connection denies. Process death denies. A request is never granted by inaction.

No silent resolution

System back, swipe and scrim taps do not resolve the request. Back is intercepted and the sheet stays; the decision is always explicit.

Danger flips the recommendation

On a danger-class request the recommendation becomes Deny, the accent fill moves to Deny, and Allow becomes the bordered control. Sizes stay equal.

Implementation review checklist

No duration string appears anywhere runStartedAt is absent.
Sessions and Chat agree on state for the same session in the same frame.
Every state is legible in greyscale — shape and label, not colour alone.
Body text ≥ 4.5:1 and every interactive boundary ≥ 3:1, in both themes.
At 200% type on 360dp: no clipped label, no fixed row height, no horizontal scroll.
Approval sheet survives back, swipe, scrim tap and rotation without resolving.
Evidence expanded with the longest test fixture still leaves Deny and Allow on screen.
Timer expiry, socket loss and app kill all record a denial server-side.
Legacy peer fixture renders no empty row, no placeholder and no invented sentence.
Danger fixture moves the accent to Deny and keeps both controls the same size.